/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package ca.inrs.ete.app.shopx.util;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.util.Log;

/**
 * Provides utility methods for communicating with the server.
 */
final public class NetworkUtilities {
	/** The tag used to log to adb console. */
	private static final String TAG = "SHOPLog.NetworkUtil";

	private NetworkUtilities() {
	}

	/**
	 * Connects to the server, authenticates the provided
	 * uri, username and password.
	 *
	 * @param uri    The server uri
	 * @param usr 	 The server account username
	 * @param pwd	 The server account password
	 * @return String The authentication token returned by the server (or null)
	 */
	public static String authenticate(String uri, String usr, String pwd)
	{
		HttpURLConnection conn = openConnection(uri, usr, pwd);
		try
		{
			String authToken = null;
			if (conn != null)
			{
				InputStream istream = conn.getInputStream();
				BufferedReader ireader = new BufferedReader(new InputStreamReader(istream));
				authToken = ireader.readLine().trim();
			}
			if ((authToken != null) && (authToken.length() > 0))
			{
				Log.v(TAG, "Successful authentication");
				return authToken;
			}
			else
			{
				Log.e(TAG, String.format("Error authenticating %s at %s", usr, uri));
				return null;
			}
		}
		catch (IOException e)
		{
			Log.e(TAG, String.format("Error reading URI: %s", uri));
			return null;
		}
		catch (Exception ex)
		{
			Log.e(TAG, String.format("Unhandled exception: %s", ex.getMessage()));
			return null;
		}
		finally
		{
			if (conn != null) conn.disconnect();
			Log.v(TAG, "getAuthtoken completing");
		}
	}

	/**
	 * Opens a connection to the uri, with usr and pwd.
	 *
	 * @param uri    The server uri
	 * @param usr 	 The server account username
	 * @param pwd	 The server account password
	 * @return HttpURLConnection The connection (or null)
	 */
	public static HttpURLConnection openConnection(String uri, String usr, String pwd)
	{
		class MyAuthenticator extends Authenticator 
		{
			private String mUsr;
			private String mPwd;
			public MyAuthenticator(String usr, String pwd)
			{
				mUsr = usr;
				mPwd = pwd;
			}
			public PasswordAuthentication getPasswordAuthentication ()
			{
				return new PasswordAuthentication (mUsr, mPwd.toCharArray());
			}
		}

		HttpURLConnection conn = null;
		try
		{
			Authenticator.setDefault(new MyAuthenticator(usr, pwd));

			URL url = new URL( uri.replace("//", "//" + usr + ":" + pwd + "@") );
			conn = (HttpURLConnection) url.openConnection();
			conn.connect();

			int http_status = conn.getResponseCode();
			if (http_status / 100 != 2) 
			{
				if (conn != null) conn.disconnect();
				conn = null;
			}
		}
		catch (MalformedURLException e)
		{
			Log.e(TAG, String.format("Invalid URI: %s", uri));
			return null;
		}
		catch (final IllegalArgumentException  e)
		{
			Log.e(TAG, "IllegalArgumentException: Invalid uri", e);
			return null;
		}
		catch (UnknownHostException e)
		{
			Log.e(TAG, e.getMessage());
			return null;
		}
		catch (IOException e)
		{
			Log.e(TAG, String.format("Error reading URI: %s", uri));
			return null;
		}
		catch (Exception ex)
		{
			Log.e(TAG, String.format("Unhandled exception: %s", ex.getMessage()));
			return null;
		}

		return conn;
	}

	/**
	 * Filter a remote file given by uri, usr and pwd according to the
	 * regex pattern.
	 *
	 * @param ptrn   The filtering pattern
	 * @param uri    The server uri
	 * @param usr 	 The server account username
	 * @param pwd	 The server account password
	 * @return ArrayList<String> Array of matching strings
	 */
	public static ArrayList<String> filterURL(Pattern ptrn, String uri, String usr, String pwd)
	{
		ArrayList<String> ret = new ArrayList<String>();

		HttpURLConnection conn = openConnection(uri, usr, pwd);
		try
		{
			InputStream         fis = conn.getInputStream();
			BufferedInputStream bis = new BufferedInputStream(fis);
			DataInputStream     dis = new DataInputStream(bis);

			String line = "";
			while((line = dis.readLine()) != null)
			{
				Matcher m = ptrn.matcher(line);
				if (m.find()) ret.add(m.group(1));
			}
		}
		catch (IOException e)
		{
			Log.e(TAG, String.format("Error reading URI: %s", uri));
			return ret;
		}
		catch (Exception ex)
		{
			Log.e(TAG, String.format("Unhandled exception: %s", ex.getMessage()));
			return ret;
		}
		finally
		{
			if (conn != null) conn.disconnect();
		}

		return ret;
	}

	/**
	 * Fetch a remote file remote file given by uri, usr and pwd according to the
	 * local file dest.
	 *
	 * @param dest   The local file name
	 * @param uri    The server uri
	 * @param usr 	 The server account username
	 * @param pwd	 The server account password
	 * @return int: 0 on success, -1 on error
	 */
	public static int fetchURL(String dest, String uri, String usr, String pwd)
	{
		Log.i(TAG, String.format("fetching file ==> %s", dest));

		HttpURLConnection conn = openConnection(uri, usr, pwd);
		InputStream  is = null;
		OutputStream os = null;
		Boolean ok = true;

		try {
			os = new FileOutputStream(dest);
			is = conn.getInputStream();

			final int BUF_SIZ = 4*1024;
			byte[] buf = new byte[BUF_SIZ];
			int l = -1;

			while((l = is.read(buf, 0, BUF_SIZ)) > 0)
			{
				os.write(buf, 0, l);
			}
		}
		catch (FileNotFoundException e)	{
			Log.e(TAG, String.format("Error opening file: %s", dest));
			ok = false;
		}
		catch (IOException e) {
			Log.e(TAG, String.format("IOException: %s", e.getMessage()));
			ok = false;
		}
		finally {
			try	{
				if (os != null) os.close();
			}
			catch (IOException e) {
			}
			if (conn != null) conn.disconnect();
		}

		return (ok) ? 0 : -1;
	}

}
